package cc.rengu.oltp.utility.util;

import cc.rengu.utility.cache.UnifiedCache;
import cc.rengu.utility.log.RgLog;
import cc.rengu.utility.log.RgLogger;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * 加载SQLMAPPER工具类
 * Created by 王成 on 2017/11/15.
 */
public class SQLMapperUtil {
    private static final RgLogger LOG = RgLog.getConsoleLogger(SQLMapperUtil.class.getName());
    /* SQL映射KEY键值 */
    private static final String SQL_MAPPER_CACHE_KEY = "id";
    /* SQL映射关系文件节点名 */
    private static final String SQL_MAPPER_CACHE_NAME = "SQL_MAPPER_CACHE";

    /**
     * 加载所有SQLMAPPER配置文件
     *
     * @throws Exception 异常
     */
    public static void loadSQLMapperInfo() throws Exception {
        /* 清空缓存 */
        UnifiedCache.clear(SQL_MAPPER_CACHE_NAME);
        Enumeration<URL> dirs;
        dirs = Thread.currentThread().getContextClassLoader().getResources("");
        while (dirs.hasMoreElements()) {
            URL url = dirs.nextElement();
            /* 获取当前URL的类型 */
            String protocol = url.getProtocol();
            if ("file".equals(protocol)) {
                String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
                loadSqlMapperCache(filePath);
            }
        }
    }

    /**
     * 将SQLMAPPER文件内容加载到共享缓存
     *
     * @param mapperFilePath 文件路径
     * @throws IOException       异常
     * @throws DocumentException Document异常
     */
    public static void loadSqlMapperCache(String mapperFilePath) throws IOException, DocumentException {
        File file = new File(mapperFilePath);
        if (file.isDirectory()) {
            File[] fileList = file.listFiles();
            if (null == fileList) {
                return;
            }
            for (File value : fileList) {
                String filePath = mapperFilePath + "/" + value.getName();
                if (value.isFile()) {
                    loadJarOrMapperFile(value, filePath);
                } else if (value.isDirectory()) {
                    loadSqlMapperCache(filePath);
                }
            }
        } else if (file.isFile()) {
            loadJarOrMapperFile(file, mapperFilePath);
        }
    }

    /**
     * @param file     文件
     * @param filePath 文件路径
     * @throws IOException       文件打开关闭IO异常
     * @throws DocumentException 文档解析异常
     */
    private static void loadJarOrMapperFile(File file, String filePath) throws IOException, DocumentException {
        if (file.getName().endsWith(".jar")) {
            try (JarFile jarFile = new JarFile(new File(filePath))) {
                Enumeration<JarEntry> entries = jarFile.entries();
                while (entries.hasMoreElements()) {
                    JarEntry entry = entries.nextElement();
                    if (!entry.isDirectory() && entry.getName().endsWith("Mapper.xml")) {
                        InputStream inputStream = null;
                        try {
                            URL url = new URL("jar:file:" + filePath + "!/" + entry.getName());
                            JarURLConnection jarConnection = (JarURLConnection) url.openConnection();
                            inputStream = jarConnection.getInputStream();
                            Map<String, String> mapperMap = parseSQLMapperFile(inputStream);
                            for (Map.Entry<String, String> mapperEntity : mapperMap.entrySet()) {
                                Object object = UnifiedCache.get(SQL_MAPPER_CACHE_NAME, mapperEntity.getKey().trim());
                                if (null == object) {
                                    int iReturnCode = UnifiedCache.set(SQL_MAPPER_CACHE_NAME, mapperEntity.getKey().trim(), mapperEntity.getValue().trim());
                                    if (iReturnCode != 0) {
                                        LOG.error("加载JAR包文件中的数据库映射文件缓存:【" + mapperEntity.getKey().trim() + "】失败!");
                                        throw new IllegalArgumentException();
                                    }
                                } else {
                                    if (object.equals(mapperEntity.getValue().trim())) {
                                        LOG.error(file.getName() + "有相同的配置重复加载：【" + mapperEntity.getKey().trim() + "】【" + mapperEntity.getValue().trim() + "】");
                                    } else {
                                        LOG.error(file.getName() + "中的MAPPER配置文件存在重复的配置项:【" + mapperEntity.getKey().trim() + "】");
                                        throw new IllegalArgumentException();
                                    }
                                }
                            }
                        } finally {
                            if (null != inputStream) {
                                inputStream.close();
                            }
                        }
                    }
                }
            }
        } else if (file.getName().endsWith("Mapper.xml")) {
            Map<String, String> mapperMap = parseSQLMapperFile(filePath);
            for (Map.Entry<String, String> entry : mapperMap.entrySet()) {
                Object object = UnifiedCache.get(SQL_MAPPER_CACHE_NAME, entry.getKey().trim());
                if (null == object) {
                    int iReturnCode = UnifiedCache.set(SQL_MAPPER_CACHE_NAME, entry.getKey().trim(), entry.getValue().trim());
                    if (iReturnCode != 0) {
                        LOG.error("加载缓存:【" + entry.getKey().trim() + "】失败!");
                        throw new IllegalArgumentException();
                    }
                } else {
                    if (object.equals(entry.getValue().trim())) {
                        LOG.error(file.getName() + "有相同的配置重复加载：【" + entry.getKey().trim() + "】【" + entry.getValue().trim() + "】");
                    } else {
                        LOG.error(file.getName() + "有重复的配置项:【" + entry.getKey().trim() + "】");
                        throw new IllegalArgumentException();
                    }
                }
            }
        }
    }

    /**
     * 加载单个SQLMAPPER文件
     * KEY：节点属性ID值
     * VALUE：节点值
     *
     * @param sqlMapperFile SQLMapper映射文件
     * @return 集合
     * @throws DocumentException 异常
     */
    private static Map<String, String> parseSQLMapperFile(String sqlMapperFile) throws DocumentException {
        Document document = getDocunemt(sqlMapperFile);
        if (null != document) {
            Map<String, String> map = new HashMap();
            /* 根据document对象获取XML文件根节点 */
            Element rootElement = document.getRootElement();
            /* 通过element对象的elementIterator方法获取迭代器 */
            Iterator nodeIterator = rootElement.elementIterator();
            /* 遍历迭代器，获取根节点信息 */
            while (nodeIterator.hasNext()) {
                Element nodeElement = (Element) nodeIterator.next();
                /* 获取当前节点的所有属性信息 */
                List<Attribute> nodeAttrList = nodeElement.attributes();
                Optional<Attribute> nodeAttrOpt = nodeAttrList.stream().filter(item -> SQL_MAPPER_CACHE_KEY.equals(item.getName())).findFirst();
                if (nodeAttrOpt.isPresent()) {
                    Attribute nodeAttr = nodeAttrOpt.get();
                    /* 将属性值及节点值登记到MAP集合对象 */
                    if (null == map.get(nodeAttr.getValue())) {
                        String nodeString = nodeElement.asXML();
                        /* 获取节点值的起始位置及截止位置 */
                        int startIndex = nodeString.indexOf(">") + 1;
                        int endIndex = nodeString.indexOf("</" + nodeElement.getName().trim());
                        /* 截取节点中的字符串值 */
                        if (startIndex >= 0 && endIndex >= 0) {
                            String keyValue = nodeString.substring(startIndex, endIndex);
                            map.put(nodeAttr.getValue().trim(), keyValue);
                        }
                    } else {
                        LOG.error("【" + sqlMapperFile.substring(sqlMapperFile.indexOf("classes") + 8) + "】存在重复配置:【" + nodeAttr.getValue() + "】");
                        throw new IllegalArgumentException();
                    }
                } else {
                    LOG.error(sqlMapperFile + "文件SQL配置项中没有配置id属性!");
                    throw new IllegalArgumentException();
                }
            }
            return map;
        } else {
            throw new DocumentException();
        }
    }

    /**
     * 加载单个SQLMAPPER文件
     * KEY：节点属性ID值
     * VALUE：节点值
     *
     * @param inputStream 文件输入流
     * @return 集合
     * @throws DocumentException 异常
     */
    private static Map<String, String> parseSQLMapperFile(InputStream inputStream) throws DocumentException {
        Document document = getDocunemt(inputStream);
        if (null != document) {
            Map<String, String> map = new HashMap();
            /* 根据document对象获取XML文件根节点 */
            Element rootElement = document.getRootElement();
            /* 通过element对象的elementIterator方法获取迭代器 */
            Iterator nodeIterator = rootElement.elementIterator();
            /* 遍历迭代器，获取根节点信息 */
            while (nodeIterator.hasNext()) {
                Element nodeElement = (Element) nodeIterator.next();
                /* 获取当前节点的所有属性信息 */
                List<Attribute> nodeAttrList = nodeElement.attributes();
                Optional<Attribute> nodeAttrOpt = nodeAttrList.stream().filter(item -> SQL_MAPPER_CACHE_KEY.equals(item.getName())).findFirst();
                if (nodeAttrOpt.isPresent()) {
                    Attribute nodeAttr = nodeAttrOpt.get();
                    /* 将属性值及节点值登记到MAP集合对象 */
                    if (null == map.get(nodeAttr.getValue())) {
                        String nodeString = nodeElement.asXML();
                        /* 获取节点值的起始位置及截止位置 */
                        int startIndex = nodeString.indexOf(">") + 1;
                        int endIndex = nodeString.indexOf("</" + nodeElement.getName().trim());
                        /* 截取节点中的字符串值 */
                        if (startIndex >= 0 && endIndex >= 0) {
                            String keyValue = nodeString.substring(startIndex, endIndex);
                            LOG.trace("KEY:" + nodeAttr.getValue().trim() + " VALUE:" + keyValue);
                            map.put(nodeAttr.getValue().trim(), keyValue);
                        }
                    } else {
                        LOG.error("存在重复配置:【" + nodeAttr.getValue() + "】");
                        throw new IllegalArgumentException();
                    }
                } else {
                    LOG.error("文件SQL配置项中没有配置id属性!");
                    throw new IllegalArgumentException();
                }
            }
            return map;
        } else {
            throw new DocumentException();
        }
    }

    /**
     * 获取文件document对象
     *
     * @param configFileName 配置文件名
     * @return document
     * @throws DocumentException 异常
     */
    private static Document getDocunemt(String configFileName) throws DocumentException {
        SAXReader reader = new SAXReader();
        /* 判断文件是否存在 */
        File file = new File(configFileName);
        if (file.exists()) {
            /*  通过reader对象的read方法加载XML文件,获取docuemnt对象 */
            return reader.read(new File(configFileName));
        }
        return null;
    }

    /**
     * 获取文件document对象
     *
     * @param inputStream 文件输入流
     * @return document
     * @throws DocumentException 异常
     */
    private static Document getDocunemt(InputStream inputStream) throws DocumentException {
        SAXReader reader = new SAXReader();
        /* 判断文件是否存在 */
        return reader.read(inputStream);
    }
}
